<template>
    <el-dialog v-model="open" width="500" @close="close" :show-close="false" header-class="dialog-header" class="dialog"
        footer-class="dialog-footer" body-class="dialog-body" close-on-press-escape>
        <el-input style="width: 100%" ref="headerSearchSelectRef" size="large" v-model="search" @input="querySearch"
            placeholder="菜单搜索，支持标题、URL关键字查询" @keydown="handleKeydown">
            <template #prefix>
                <el-icon color="#409EFC" class="no-inherit">
                    <Search />
                </el-icon>
            </template>
        </el-input>
        <div>
            <div class="list-item" v-show="panel == 0">
                <el-text type="primary" v-show="search == ''" class="left">搜索历史</el-text>
                <el-text type="primary" v-show="search == ''" class="right" @click.stop="clearHistory">清空历史</el-text>
            </div>
            <el-scrollbar height="200px">
                <div v-show="panel == 0">
                    <div class="history-item list-item" v-for="(history, index) in histories" :key="index"
                        @click.stop="handleClick(history)" :class="{ 'highlight': highlightIndex === index }"
                        @mouseenter="highlightItem(index)" @mouseleave="clearHighlight" ref="historyItemRefs">
                        <div class="left">
                            <el-text type="primary"> {{ history.title.join(' > ') }}</el-text>
                            <div class="path">{{ history.path }}</div>
                        </div>
                        <div class="right">
                            <el-icon style="width: 20px;height: 20px;" @click.stop="removeHistory(history)">
                                <Close />
                            </el-icon>
                        </div>
                    </div>
                </div>
                <div v-show="panel == 1">
                    <div class="option-item list-item" v-for="(option, index) in options" :key="index"
                        @click.stop="handleClick(option.item)" :class="{ 'highlight': highlightIndex === index }"
                        @mouseenter="highlightItem(index)" @mouseleave="clearHighlight" ref="resultItemRefs">
                        <div class="left">
                            <el-text type="primary">{{ option.item.title.join(' > ') }}</el-text>
                            <div class="path">{{ option.item.path }}</div>
                        </div>
                        <button class="right icon-btn" :title="option.item.hasGather ? '添加到快捷入口' : '从快捷入口移除'">
                            <svg-icon icon-class="star" :class-name="`star ${option.item.hasGather ? 'fill-star' : ''}`"
                                @click.stop="handleStar(option.item.id)" />
                        </button>
                    </div>
                    <div v-show="options.length <= 0">
                        <el-empty :image-size="100" description="暂无数据" />
                    </div>
                </div>
            </el-scrollbar>
        </div>

        <template #footer>
            <div class="DocSearch-Commands">
                <span class="DocSearch-Command">
                    <span class="DocSearch-Command-Keys">
                        <kbd class="DocSearch-Command-Key">Enter</kbd>
                    </span>
                    <span class="DocSearch-Command-Text">选择</span>
                </span>

                <span class="DocSearch-Command">
                    <span class="DocSearch-Command-Keys">
                        <kbd class="DocSearch-Command-Key">Up</kbd>
                    </span>
                    <span class="DocSearch-Command-Text">上移</span>
                </span>
                <span class="DocSearch-Command">
                    <span class="DocSearch-Command-Keys">
                        <kbd class="DocSearch-Command-Key">Down</kbd>
                    </span>
                    <span class="DocSearch-Command-Text">下移</span>
                </span>
            </div>

        </template>
    </el-dialog>
</template>

<script setup>
import Fuse from 'fuse.js'
import { getNormalPath } from '@/utils/ruoyi'
import { isHttp } from '@/utils/validate'
import usePermissionStore from '@/store/modules/permission'
import useShortcutStore from '@/store/modules/shortcut'




const open = defineModel({ required: true, default: false })

const shortcutsStore = useShortcutStore();
const shortcuts = computed(() => shortcutsStore.shortcuts)
const { proxy } = getCurrentInstance();
const search = ref('');
const options = ref([]);
const searchPool = ref([]);
const fuse = ref(undefined);
const headerSearchSelectRef = ref(null);
const router = useRouter();
const routes = computed(() => usePermissionStore().routes);
const historyKey = ref('historySearch');
const histories = ref([]);
const highlightIndex = ref(null);
const panel = ref(0) // 0:历史记录，1：搜索结果
const historyItemRefs = ref([]);
const resultItemRefs = ref([]);


//<!------------- 事件定义 ------------->



const emits = defineEmits(['star'])
function handleStar(menuId) {
    let menu = options.value.find(opt => opt.item.id == menuId).item;
    let func = !menu.hasGather ? shortcutsStore.addShortcut : shortcutsStore.removeShortcut;
    menu.hasGather = !menu.hasGather;
    func({ menuId }).then(res => {
        let msg = menu.hasGather ? "添加成功" : "移除成功"
        proxy.$modal.msgSuccess(msg)
        emits("star", menuId);
    }).catch(item => {
        menu.hasGather = !menu.hasGather;
    })
}




//<!------------- 快捷键注册 ------------->


const moveDown = () => {
    let length = panel.value == 0 ? histories.value.length : options.value.length;
    if (highlightIndex.value == null) highlightIndex.value = -1;
    highlightIndex.value = (highlightIndex.value + 1) % length;
    return highlightIndex.value;
}

const moveUp = () => {
    let length = panel.value == 0 ? histories.value.length : options.value.length;
    highlightIndex.value = (highlightIndex.value - 1 + length) % length;
    return highlightIndex.value;
}



const handleKeydown = (e) => {
    if (e.key == "ArrowDown") {
        let index = moveDown();
        scrollToHighlightedItem(index)
    } else if (e.key == "ArrowUp") {
        let index = moveUp();
        scrollToHighlightedItem(index)
    } else if (e.key == "Enter") {
        let index = highlightIndex.value;
        let item = panel.value == 0 ? histories.value[index] : options.value[index].item;
        handleClick(item)
    } else if (e.ctrlKey && e.key == "k") {
        e.preventDefault();
        close();
    }
}

const scrollToHighlightedItem = (index) => {
    let item = panel.value == 0 ? historyItemRefs.value[index] : resultItemRefs.value[index];
    if (item) {
        item.scrollIntoView({
            behavior: "smooth",
            block: "end",
        });
    }
};


const onShow = () => {
    console.log("show search input")
    open.value = true;
    histories.value = getHistoryList();
    if (open.value) {
        //解决无法获取焦点问题
        nextTick(() => {
            setTimeout(() => {
                headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
            }, 1)
        });
    }
}


function close() {
    headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
    options.value = []
    open.value = false;
    search.value = ''
    highlightIndex.value = null;
    panel.value = 0;
}
function change(val) {
    const path = val.path;
    const query = val.query;
    if (isHttp(path)) {
        // http(s):// 路径新窗口打开
        const pindex = path.indexOf("http");
        window.open(path.substr(pindex, path.length), "_blank");
    } else {
        open.value = false;
        if (query) {
            router.push({ path: path, query: JSON.parse(query) });
        } else {
            router.push(path)
        }
    }

    search.value = ''
    options.value = []
    nextTick(() => {
        open.value = false;
    })
}

const handleClick = (val) => {
    appendHistory(val);
    change(val)
    // handleLove();
}

const highlightItem = (index) => {
    highlightIndex.value = index;
};

const clearHighlight = () => {
    highlightIndex.value = null;
};


const clearHistory = (item) => {
    proxy.$cache.local.remove(historyKey.value);
    histories.value = [];
}

const appendHistory = (item) => {
    let maxLength = 20;
    const exists = histories.value.some(history => history.id === item.id);
    if (!exists) {
        if (histories.value.length >= maxLength) {
            histories.value.shift();
        }
        histories.value.push(item);
        proxy.$cache.local.setJSON(historyKey.value, histories.value);
    }
}

const removeHistory = (item) => {
    histories.value = histories.value.filter(s => s.id != item.id);
    proxy.$cache.local.setJSON(historyKey.value, histories.value);
}

const getHistoryList = () => {
    let list = proxy.$cache.local.getJSON(historyKey.value) || [];
    return list;
}


function initFuse(list) {
    fuse.value = new Fuse(list, {
        shouldSort: true,
        threshold: 0.4,
        location: 0,
        distance: 100,
        minMatchCharLength: 1,
        keys: [{
            name: 'title',
            weight: 0.7
        }, {
            name: 'path',
            weight: 0.3
        }]
    })
}
// Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title
function generateRoutes(routes, basePath = '', prefixTitle = []) {
    let res = []
    for (const r of routes) {
        // skip hidden router
        if (r.hidden) { continue }
        const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
        const data = {
            path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
            title: [...prefixTitle],
            id: r.id
        }

        if (r.meta && r.meta.title && !r.children) {
            data.title = [...data.title, r.meta.title]

            if (r.redirect !== 'noRedirect') {
                // only push the routes with title
                // special case: need to exclude parent router without redirect
                res.push(data)
            }
        }
        if (r.query) {
            data.query = r.query
        }

        // recursive child routes
        if (r.children) {
            const tempRoutes = generateRoutes(r.children, data.path, data.title)
            if (tempRoutes.length >= 1) {
                res = [...res, ...tempRoutes]
            }
        }
    }
    return res
}
function querySearch(query) {
    if (query !== '') {
        options.value = fuse.value.search(query)
        options.value.forEach(opt => {
            opt.item.hasGather = shortcuts.value.some(s => s.menuId == opt.item.id);
        });
        panel.value = 1;
    } else {
        panel.value = 0;
    }
    search.value = query;
    console.log(options.value, shortcuts.value);
}


onMounted(() => {
    shortcutsStore.loadShortcuts();
    searchPool.value = generateRoutes(routes.value);
})

watchEffect(() => {
    searchPool.value = generateRoutes(routes.value)
})


watch(searchPool, (list) => {
    initFuse(list)
})

watch(open, (newValue) => {
    if (newValue) {
        onShow();
    }
})



</script>

<style lang='scss' scoped>
.header-search {
    // .search-icon {
    //   cursor: pointer;
    //   font-size: 18px;
    //   vertical-align: middle;
    // }

    .list-item {
        transition: background-color 0.3s ease;
        display: flex;
        line-height: 25px;
        padding: 2px 10px;
        width: 100%;
        cursor: pointer;
        align-items: center;

        .left {
            flex: 1;
        }

        .path {
            line-height: 20px;
        }

        .right,
        .icon-btn {
            display: flex;
            align-items: center;
            gap: 4px;
            padding: 2px;
            flex: 0 auto;
            border: 0;
            border-radius: 50%;
            cursor: pointer;
            background: transparent;

            :hover {
                border: 0;
                border-radius: 50%;
                background-color: var(--el-color-info-light-3);
            }
        }

        .star {
            width: 20px;
            height: 20px;
            fill: none;
            stroke-width: 30px;
            stroke: white;

            &:hover {
                fill: #f7ba2a
            }
        }

        .fill-star {
            fill: #f7ba2a !important;
        }
    }

    .highlight {
        background-color: var(--el-color-info-light-9);
    }


}

.DocSearch-Commands {
    display: flex;
    gap: 20px;
    padding: 0 8px;
    align-items: center;
    height: 100%;
    color: var(--el-color-info);

    .DocSearch-Command {
        display: flex;
        align-items: center;
        font-size: 12px;
        gap: 8px;

        .DocSearch-Command-Keys {
            display: flex;
            flex-direction: row;
            gap: 2px;
            min-width: auto;
            box-sizing: border-box;
            border: 1px solid var(--el-color-info-light-9);
            border-radius: 4px;
            padding: 0 6px;
            font-family: inherit;
            font-size: 12px;
            height: 22px;
            line-height: 22px;
            font-weight: 500;
            transition: color .5s, border-color .5s;
        }


        .DocSearch-Command-Key {
            font-weight: bold;
            align-items: center;
            border-radius: 3px;
            display: flex;
            height: 18px;
            justify-content: center;
            position: relative;
            padding: 0 0 2px;
            border: 0;
            top: -1px;
            align-items: baseline;
        }

        .DocSearch-Command-Text {
            font-family: inherit;
        }
    }


}
</style>

<style lang="scss">
.header-search {
    .dialog-header {
        padding: 0;
    }

    .dialog-body {
        padding: var(--el-dialog-padding-primary);
    }

    .dialog {
        padding: 0;
    }

    .dialog-footer {
        text-align: left;
        height: 40px;
        line-height: 40px;
        background-color: rgb(0 0 0 / 60%);
        padding-top: 0;
        // margin-top: var(--el-dialog-padding-primary);
    }
}
</style>